# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.930.24.2 -> 1.930.24.3
#	drivers/char/serial.c	1.33    -> 1.34   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/02/13	bjorn_helgaas@hp.com	1.930.23.3
# ia64: Use has_8259 rather than initdata.
# --------------------------------------------
# 03/02/13	bjorn_helgaas@hp.com	1.930.23.4
# ia64: Really remove ACPI SPCR parsing.
# --------------------------------------------
# 03/02/13	hch@sgi.com	1.930.18.2
# [SPARC]: Add xattr syscalls.
# --------------------------------------------
# 03/02/13	davem@nuts.ninka.net	1.930.17.10
# [NET]: Fix length in skb_padlen.
# --------------------------------------------
# 03/02/13	fubar@us.ibm.com	1.930.17.11
# [BONDING]: Add MAINTAINERS entry.
# --------------------------------------------
# 03/02/13	bjorn_helgaas@hp.com	1.930.23.5
# Cset exclude: eranian@frankl.hpl.hp.com[helgaas]|ChangeSet|20030103231109|26349
# --------------------------------------------
# 03/02/13	eranian@frankl.hpl.hp.com	1.930.23.6
# ia64: new perfmon patch for 2.4.20
# 
# Here is a new complete patch for perfmon against 2.4.20. Note that this 
# patch supersedes the one I sent you last week. This patch does:
# 
# 	- fix the SMP system-wide monitoring problem
# 	- add support for excluding idle tasks from system wide monitoring sessions
# 	- regroup all __asm__ into a set of inline functions: cleaner and will make it
# 	  easier for the Secure Linux folks (Tom Cristian).
# 	- optimize cost of psr fixup in system wide (simplify local_cpu_info).
# 	- reestablish restriction of pfm_write_{pmds,pmcs} when passing the pid
#           of another process. We do not support this yet.
# 	- update perfmon revision to 1.3
# --------------------------------------------
# 03/02/13	bjorn_helgaas@hp.com	1.930.23.7
# ia64: fix perfmon typo (PFM_CPU_SYST_WIDE should be PFM_CPUINFO_SYST_WIDE).
# --------------------------------------------
# 03/02/13	bame@fc.hp.com	1.930.24.3
# HP multiport serial card fixes.
# 
# Someday HP'll release boxes which can contain more than one
# ECI/MP/Diva multiport serial card.  This patch, unlike the earlier
# one I sent, supports multiple instances of the card.
# --------------------------------------------
#
diff -Nru a/drivers/char/serial.c b/drivers/char/serial.c
--- a/drivers/char/serial.c	Wed Oct  8 09:05:43 2003
+++ b/drivers/char/serial.c	Wed Oct  8 09:05:43 2003
@@ -257,6 +257,10 @@
 
 static struct timer_list serial_timer;
 
+#define HP_DIVA_CHECKTIME (1*HZ)
+static struct timer_list hp_diva_timer;
+static int hp_diva_count = 0;
+
 /* serial subtype definitions */
 #ifndef SERIAL_TYPE_NORMAL
 #define SERIAL_TYPE_NORMAL	1
@@ -793,6 +797,41 @@
 }
 
 #ifdef CONFIG_SERIAL_SHARE_IRQ
+static inline int is_hp_diva_info(struct async_struct *info) 
+{
+    struct pci_dev *dev = info->state->dev;
+    return (dev && dev->vendor == PCI_VENDOR_ID_HP &&
+		dev->device == PCI_DEVICE_ID_HP_SAS);
+}
+
+static inline int is_hp_diva_irq(int irq)
+{
+    struct async_struct *info = IRQ_ports[irq];
+    return (info && is_hp_diva_info(info));
+}
+
+/*
+ * It is possible to "use up" transmit empty interrupts in some
+ * cases with HP Diva cards.  Figure out if there _should_ be a
+ * transmit interrupt and if so, return a suitable iir value so
+ * that we can recover when called from rs_timer().
+ */
+static inline int hp_diva_iir(int irq, struct async_struct *info)
+{
+	int iir = serial_in(info, UART_IIR);
+
+	if (is_hp_diva_info(info) &&
+		(iir & UART_IIR_NO_INT) != 0 &&
+		(info->IER & UART_IER_THRI) != 0 &&
+		(info->xmit.head != info->xmit.tail || info->x_char) &&
+		(serial_in(info, UART_LSR) & UART_LSR_THRE) != 0) {
+		    iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
+		    iir |= UART_IIR_THRI;
+	}
+
+	return iir;
+}
+
 /*
  * This is the serial driver's generic interrupt routine
  */
@@ -823,7 +862,7 @@
 
 	do {
 		if (!info->tty ||
-		    ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) {
+		    ((iir=hp_diva_iir(irq, info)) & UART_IIR_NO_INT)) {
 			if (!end_mark)
 				end_mark = info;
 			goto next;
@@ -1092,9 +1131,11 @@
 #ifdef CONFIG_SERIAL_SHARE_IRQ
 			if (info->next_port) {
 				do {
-					serial_out(info, UART_IER, 0);
-					info->IER |= UART_IER_THRI;
-					serial_out(info, UART_IER, info->IER);
+					if (!is_hp_diva_info(info)) {
+						serial_out(info, UART_IER, 0);
+						info->IER |= UART_IER_THRI;
+						serial_out(info, UART_IER, info->IER);
+					}
 					info = info->next_port;
 				} while (info);
 #ifdef CONFIG_SERIAL_MULTIPORT
@@ -1126,6 +1167,33 @@
 }
 
 /*
+ * This subroutine is called when the hp_diva_timer goes off.  In certain
+ * cases (multiple gettys in particular) Diva seems
+ * to issue only a single transmit empty interrupt instead of one each
+ * time THRI is enabled, causing interrupts to be "used up".  This
+ * serves to poll the Diva UARTS more frequently than rs_timer() does.
+ */
+static void hp_diva_check(unsigned long dummy)
+{
+	static unsigned long last_strobe;
+	unsigned long flags;
+	int i;
+
+	if (time_after_eq(jiffies, last_strobe + HP_DIVA_CHECKTIME)) {
+		for (i = 0; i < NR_IRQS; i++) {
+			if (is_hp_diva_irq(i)) {
+			    save_flags(flags); cli();
+			    rs_interrupt(i, NULL, NULL);
+			    restore_flags(flags);
+			}
+		}
+	}
+	last_strobe = jiffies;
+	mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME);
+}
+
+
+/*
  * ---------------------------------------------------------------
  * Low level utility subroutines for the serial driver:  routines to
  * figure out the appropriate timeout for an interrupt chain, routines
@@ -4281,6 +4349,12 @@
 		break;
 	}
 
+	if (hp_diva_count++ == 0) {
+		init_timer(&hp_diva_timer);
+		hp_diva_timer.function = hp_diva_check;
+		mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME);
+	}
+
 	return 0;
 }
 
@@ -5719,6 +5793,8 @@
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
 	del_timer_sync(&serial_timer);
+	if (hp_diva_count > 0)
+		del_timer_sync(&hp_diva_timer);
 	save_flags(flags); cli();
         remove_bh(SERIAL_BH);
 	if ((e1 = tty_unregister_driver(&serial_driver)))